package com.samphanie.security.security.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.samphanie.common.enums.ResultCode;
import com.samphanie.common.service.IRedisService;
import com.samphanie.security.domain.OauthClient;
import com.samphanie.security.security.properties.AuthConstants;
import com.samphanie.security.security.service.OauthClientService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * @author ZSY
 * @email 1451691457@qq.com
 */
@Slf4j
@Setter
@Service
public class ClientDetailsServiceImpl extends JdbcClientDetailsService {

    @Resource
    private DataSource dataSource;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private IRedisService redisService;
    @Resource
    private OauthClientService clientService;

    public ClientDetailsServiceImpl(DataSource dataSource) {
        super(dataSource);
    }

    @Bean
    @Primary
    public ClientDetailsServiceImpl clientDetailService() {
        ClientDetailsServiceImpl clientDetailsService = new ClientDetailsServiceImpl(dataSource);
        clientDetailsService.setRedisService(redisService);
        clientDetailsService.setPasswordEncoder(passwordEncoder);
        return clientDetailsService;
    }

    /**
     * 从redis里读取ClientDetails
     *
     * @param clientId 客户端ID
     * @return ClientDetails
     * @throws InvalidClientException 非法客户端异常
     */
    @Override
    public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException {
        BaseClientDetails clientDetails = (BaseClientDetails) redisService.get(clientKey(clientId));
        if (ObjectUtil.isNull(clientDetails)) {
            clientDetails = (BaseClientDetails) getCacheClient(clientId);
        }
        clientDetails.getAuthorizedGrantTypes().add(AuthConstants.CLIENT_CREDENTIALS);
        return clientDetails;
    }

    /**
     * 自定义语句查询，并将数据同步至redis
     *
     * @param clientId 客户端ID
     * @return ClientDetails
     */
    private ClientDetails getCacheClient(String clientId) {
        // clientDetails = super.loadClientByClientId(clientId);
        OauthClient client = clientService.loadClientByClientId(clientId)
                .orElseThrow(() -> {
                    log.error("No client with requested id: {}", clientId);
                    return new InvalidClientException(ResultCode.USER_ERROR_A0231.getMessage());
                });
        BaseClientDetails clientDetails = new BaseClientDetails(
                client.getClientId(),
                client.getResourceIds(),
                client.getScope(),
                client.getAuthorizedGrantTypes(),
                client.getAuthorities(),
                client.getWebServerRedirectUri()
        );
        clientDetails.setClientSecret(client.getClientSecret());
        clientDetails.setAccessTokenValiditySeconds(client.getAccessTokenValidity());
        clientDetails.setRefreshTokenValiditySeconds(client.getRefreshTokenValidity());
        if (ObjectUtil.isNotNull(clientDetails)) {
            redisService.set(clientKey(clientId), clientDetails);
            log.info("Cache clientId:{}, clientDetails:{}", clientId, clientDetails);
        }
        return clientDetails;
    }

    private String clientKey(String clientId) {
        return AuthConstants.CLIENT_TABLE + ":" + clientId;
    }
}
